/*
    The MIT License

    Copyright (c) 2010 IFMO/GameDev Studio

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.
*/


#pragma once

/*-----------------------------------------------------------------------------
	Core interfaces :
-----------------------------------------------------------------------------*/

typedef HardRef<class IGeometryFactory	>	IPxGeometryEngine;
typedef HardRef<class ITriMesh			>	IPxTriMesh;
typedef HardRef<class IAnimTrack		>	IPxAnimTrack;

typedef HardRef<class IScene			>	IPxScene;
typedef HardRef<class ISceneNode		>	IPxSceneNode;
typedef HardRef<class ISceneIt			>	IPxSceneIt;

/*-----------------------------------------------------------------------------
	Geometry engine :
-----------------------------------------------------------------------------*/

class	IGeometryFactory : public ICoreObject {
	public:
		virtual ITriMesh		*CreateTriMesh			( uint format ) = 0;
		virtual IScene			*CreateScene			( void ) = 0;
		virtual ISceneNode		*CreateSceneNode		( void ) = 0;
		
		virtual IAnimTrack		*CreateAnimTrack		( void ) = 0;

		virtual void			ClearSceneCache			( void ) = 0;		
		virtual IPxScene		LoadSceneFromFile		( const EString file_scene_node_path ) = 0;
		virtual	IPxSceneNode	LoadSceneNodeFromFile	( const EString file_scene_node_path ) = 0;
		virtual IPxTriMesh		LoadMeshFromFile		( const EString file_scene_node_path ) = 0;
		virtual	IPxAnimTrack	LoadAnimTrack			( const EString path ) = 0;
		
		virtual IPxTriMesh		CreatePlane				( float size_x, float size_y, uint subdiv_x, uint subdiv_y ) = 0;
		virtual IPxTriMesh		CreateCube				( float size_x, float size_y, float size_z ) = 0;
		virtual IPxTriMesh		CreateCylinder			( float height, float radius, uint subdiv_axis ) = 0;
		virtual IPxTriMesh		CreateSphere			( float radius, uint subdiv_axis, uint subdiv_height ) = 0;
		virtual IPxTriMesh		CreateCubeFromBBox		( const EBBox &bbox ) = 0;

	};
	


/*-----------------------------------------------------------------------------
	Animation track :
-----------------------------------------------------------------------------*/

class IAnimTrack : public ICoreObject {
	public:
		virtual void			SetFPS			( float fps ) = 0;
		virtual float			GetFPS			( void ) const = 0;
	
		virtual void			SetTrackSize	( uint curve_num, uint length ) = 0;
		virtual uint			GetTrackLength	( void ) const = 0;
		virtual uint			GetCurveNum		( void ) const = 0;
		virtual uint			GetCurveByName	( const EString &name ) const = 0;
		
		virtual void			SetCurveParent	( uint child_id, uint parent_id ) = 0;
		virtual uint			GetCurveParent	( uint child_id ) const = 0;
		virtual void			SetCurveName	( uint curve, const EString &name ) = 0;
		virtual EString			GetCurveName	( uint curve ) const = 0;
		
		virtual void			SetSample		( uint curve, uint sample, const EPoint &pos, const EQuaternion &orient ) = 0;
		
		virtual void			GetSample		( uint curve, int sample,							EPoint &pos, EQuaternion &orient, bool repeat ) const = 0;
		virtual void			LerpSamples		( uint curve, int sample0, int sample1, float frac, EPoint &pos, EQuaternion &orient, bool repeat ) const = 0;
		virtual void			LerpSamples		( uint curve, float frac_sample,					EPoint &pos, EQuaternion &orient, bool repeat ) const = 0;
		virtual void			GetSampleAtTime	( uint curve, float time,							EPoint &pos, EQuaternion &orient, bool repeat ) const = 0;
	
		virtual IPxAnimTrack	Clone			( void ) const = 0;
		virtual void			WriteXML		( rapidxml::xml_node<> *xnode ) const = 0;
		virtual void			ReadXML			( rapidxml::xml_node<> *xnode ) = 0;

		virtual void			BuildAnimFrame			( const IPxTriMesh mesh, float frac_sample, bool repeat ) = 0;
		virtual void			AddTransform			( uint curve, const EVector &translate, const EQuaternion &rotate ) = 0;
		virtual EMatrix		*	BuildGlobalMatricies	( const IPxTriMesh mesh ) = 0;
	};


/*-----------------------------------------------------------------------------
	Mesh format
-----------------------------------------------------------------------------*/

const uint GE_MESH_POSITION			=	0x0001;
const uint GE_MESH_NORMAL			=	0x0002;
const uint GE_MESH_COLOR_0			=	0x0004;
const uint GE_MESH_COLOR_1			=	0x0008;
const uint GE_MESH_UV_0				=	0x0010;
const uint GE_MESH_UV_1				=	0x0020;
const uint GE_MESH_TANGENT			=	0x0040;
const uint GE_MESH_BINORMAL			=	0x0080;
const uint GE_MESH_JOINT_WEIGHTS	=	0x0100;
const uint GE_MESH_JOINT_IND		=	0x0200;

const uint GE_MAX_JOINT_NUM			=	48;

/*-----------------------------------------------------------------------------
	Vertex and Shading group :
-----------------------------------------------------------------------------*/

class EVertex {
	public:
		EVertex		( void ) {
			joint_inds[0]	=	0;
			joint_inds[1]	=	0;
			joint_inds[2]	=	0;
			joint_inds[3]	=	0;
			joint_weights[0]	=	0;
			joint_weights[1]	=	0;
			joint_weights[2]	=	0;
			joint_weights[3]	=	0;
		}
		
		~EVertex	( void ) {
		}
	public:
		EPoint		position;
		EVector		normal;
		EColor		color0;
		EColor		color1;
		ETexCoord	uv0;
		ETexCoord	uv1;
		EVector		tangent;
		EVector		binormal;
		uint		joint_inds[4];
		float		joint_weights[4];
	};
	
	
class EShadingGroup {
	public:
		EShadingGroup	( void ) {
			start = num = 0;
			shader = "";
		}
		
		~EShadingGroup	( void ) {
		}
	public:
		//	NOTE : 'start' and 'num' are related to primitives 
		//	but not to indices or vertices.
		uint	start;
		uint	num;
		EString	shader;
	};	

enum ERotateOrder {
	XYZ,
	YZX,
	ZXY,
	XZY,
	YXZ,
	ZYX
};

enum ELimitFlags_s {
	TRANS_LIMIT_X_MIN	= 0x00000001,
	TRANS_LIMIT_X_MAX	= 0x00000002,
	TRANS_LIMIT_Y_MIN	= 0x00000004,
	TRANS_LIMIT_Y_MAX	= 0x00000008,
	TRANS_LIMIT_Z_MIN	= 0x00000010,
	TRANS_LIMIT_Z_MAX	= 0x00000020,
	ROT_LIMIT_X_MIN		= 0x00000040,
	ROT_LIMIT_X_MAX		= 0x00000080,
	ROT_LIMIT_Y_MIN		= 0x00000100,
	ROT_LIMIT_Y_MAX		= 0x00000200,
	ROT_LIMIT_Z_MIN		= 0x00000400,
	ROT_LIMIT_Z_MAX		= 0x00000800,
	SCALE_LIMIT_X_MIN	= 0x00001000,
	SCALE_LIMIT_X_MAX	= 0x00002000,
	SCALE_LIMIT_Y_MIN	= 0x00004000,
	SCALE_LIMIT_Y_MAX	= 0x00008000,
	SCALE_LIMIT_Z_MIN	= 0x00010000,
	SCALE_LIMIT_Z_MAX	= 0x00020000,
};

class EJoint {
	public:
		EJoint	( void ) {
			name				=	"";
			parent_index		=	0;
			rotate_order		=	XYZ;
			angular_limit_min	=	EVector(-PI, -PI, -PI);
			angular_limit_max	=	EVector( PI,  PI,  PI);
		}
		
		~EJoint	( void ) {
		}
		
		EString			name;			//	joint's name
		EQuaternion		orient;			//	Maya's joint orient
		EPoint			position;		//	Maya's joint position
		EQuaternion		def_orient;		//	default rotation of the joint (when curve is NULL)
		EPoint			def_position;	//	default position of the joint (when curve is NULL)
		EQuaternion		bind_orient;
		EPoint			bind_pos;
		int				parent_index;	//	index of parent joint
		ERotateOrder	rotate_order;
		int				limit_flags;

		// Constraints
		EVector angular_limit_min;
		EVector angular_limit_max;
	};	
	

/*------------------------------------------------------------------------------
	Indexed triangle mesh :
------------------------------------------------------------------------------*/

class ITriMeshHitCB {
	public:
		virtual void Callback( EVertex &point, float fraction, bool into_front_side ) = 0;
	};

class	ITriMesh : public ICoreObject {			
	public:
		virtual uint			GetFormat		( void ) const = 0;
		virtual void			SetFormat		( uint format ) = 0;
	
		virtual void			SetVertexNum	( uint num ) = 0;
		virtual uint			GetVertexNum	( void ) const = 0;
		virtual uint			AddVertex		( const EVertex &v ) = 0;
		virtual void			SetVertex		( uint index, const EVertex &v ) = 0;
		virtual EVertex			GetVertex		( uint index ) const = 0;
		
		virtual uint			AddUniqueVertex	( const EVertex &v, float tolerance ) = 0;
		
		virtual void			SetTriangleNum	( uint num ) = 0;
		virtual uint			GetTriangleNum	( void ) const = 0;
		virtual uint			AddTriangle		( uint i0, uint i1, uint i2 ) = 0;
		virtual uint			AddTriangle		( const EVertex& v0, const EVertex& v1, const EVertex & v2) = 0;
		virtual void			SetTriangle		( uint index, uint i0,  uint i1,  uint i2 ) = 0;
		virtual void			GetTriangle		( uint index, uint &i0, uint &i1, uint &i2 ) const = 0;

		virtual void			AttachSG		( const EShadingGroup &sg ) = 0;
		virtual void			DetachSG		( uint index ) = 0;
		virtual uint			GetSGNum		( void ) const = 0;
		virtual void			SetSGNum		( uint num ) = 0;
		virtual EShadingGroup	GetSG			( uint index ) const = 0;
		virtual void			SetSG			( uint index, const EShadingGroup &sg ) = 0;
		virtual void			SetSGToAllTris	( const EString &name ) = 0;
		
		virtual IPxTriMesh		Clone			( void ) const = 0;
		
		virtual void			WriteXML		( rapidxml::xml_node<> *xnode ) const = 0;
		virtual void			ReadXML			( rapidxml::xml_node<> *xnode ) = 0;
		
		//	sophistic stuff :
		virtual void			Transform		( const EMatrix &T ) = 0;
		virtual void			GlueSGs			( void ) = 0;
		virtual void			MergeVertices	( void ) = 0;
		virtual void			Split			( const EPlane &plane, IPxTriMesh left, IPxTriMesh right ) = 0;
		virtual void			ComputeNormals	( void ) = 0;
		virtual void			ComputeTangents	( void ) = 0;
		virtual EBBox			ComputeBBox		( void ) const = 0;
		
		virtual EPoint			TriangleCenter	( uint index ) const = 0;
		virtual EVector			TriangleNormal	( uint index ) const = 0;
		virtual float			TriangleArea	( uint index ) const = 0;

		virtual void			Merge			( IPxTriMesh mesh) = 0;
		virtual bool			IsEmpty			( void ) const = 0;
		virtual void			Clear			( void ) = 0;
		virtual bool			RayIntersect	( const EPoint &origin, const EVector &direction, EVertex &result) const = 0;
		virtual bool			RayIntersect	( const EPoint &origin, const EVector &direction, ITriMeshHitCB *report) const = 0;
		virtual bool			GetDecal		( const EPoint &origin, const EVector &direction, float size, IPxTriMesh result, EVertex &v) = 0;
		
		//	animation stuff :
		virtual bool			IsSkinned		( void ) const = 0;
		virtual void			AddJoint		( EJoint joint ) = 0;

		virtual uint			GetJointNum		( void ) const = 0;
		virtual EJoint			*GetJoint		( uint index ) = 0;
		virtual const EJoint	*GetJoint		( uint index ) const = 0;
		virtual uint			GetJointIDByName( const EString name ) = 0;

		virtual IPxTriMesh		CreateLimb		( EString joint_name, float threshold, bool include_childs ) = 0;
	};
	

/*-----------------------------------------------------------------------------
	Scene graph :	
-----------------------------------------------------------------------------*/

enum ESceneIterType_s {
		SCENE_ITERATOR_BFS,
		SCENE_ITERATOR_DFS,
	};

class IScene : public ICoreObject {
	public:
		virtual IPxSceneNode		Root			( void ) = 0;
		virtual const IPxSceneNode	Root			( void ) const = 0;
		virtual void				WriteXML		( rapidxml::xml_node<> *xnode ) const = 0;
		virtual void				ReadXML			( rapidxml::xml_node<> *xnode ) = 0;

		virtual void				Clear			( void ) = 0;
		virtual void				BreakHierarchy	( IPxSceneNode node ) = 0;
		
		virtual IPxSceneIt			GetIterator		( ESceneIterType_s iter_type ) = 0;
		virtual	IPxSceneNode		GetNodeByPath	( const char *path ) = 0;
	};
	

class	ISceneIt : public ICoreObject {
	public:
		virtual void			Reset		( void ) = 0;
		virtual bool			Done		( void ) = 0;
		virtual void			Next		( void ) = 0;
		virtual IPxSceneNode	Current		( void ) = 0;
	};	
	
	
class ISceneNode : public ICoreObject {
	public:						
		virtual IPxTriMesh			GetMesh			( void ) = 0;
		virtual const IPxTriMesh	GetMesh			( void ) const = 0;
		virtual void				SetMesh			( IPxTriMesh mesh ) = 0;
		
		virtual EString				GetScenePath	( void ) const = 0;

		virtual EString				GetName			( void ) const = 0;
		virtual void				SetName			( EString name ) = 0;
		virtual EString				GetPath			( void ) const = 0;
		virtual EString				GetClass		( void ) const = 0;
		virtual void				SetClass		( EString classname ) = 0;
		virtual const char			*GetAffector	( const char *key, const char *def_value ) const = 0;
		virtual EString				GetAffector		( void ) const = 0;
		virtual void				SetAffector		( const char *affector_string ) = 0;
		virtual void				AddAffector		( const char *affector_string ) = 0;

		virtual void				GetLocalPose	( EPoint &pos, EQuaternion &orient ) const = 0;
		virtual void				SetLocalPose	( const EPoint &pos, const EQuaternion &orient ) = 0;
		virtual void				GetGlobalPose	( EPoint &pos, EQuaternion &orient ) const = 0;

		//	hierarchy stuff :
		virtual void				AttachChild		( IPxSceneNode node ) = 0;
		virtual void				DetachChild		( IPxSceneNode node ) = 0;		
		virtual void				DetachChild		( uint index ) = 0;	
		virtual void				DetachAll		( void ) = 0;
		
		virtual uint				GetChildNum		( void ) const = 0;
		virtual IPxSceneNode		GetChild		( uint index ) = 0;
		virtual const IPxSceneNode	GetChild		( uint index ) const = 0;
		virtual IPxSceneNode		GetChild		( EString name ) = 0;
		virtual const IPxSceneNode	GetChild		( EString name ) const = 0;
		
		virtual void				SetParent		( IPxSceneNode parent ) = 0;
		virtual IPxSceneNode		GetParent		( void ) = 0;
		virtual const IPxSceneNode	GetParent		( void ) const = 0;

		//	xml stuff :
		virtual void			WriteXML		( rapidxml::xml_node<> *xnode ) const = 0;
		virtual void			ReadXML			( rapidxml::xml_node<> *xnode ) = 0;
	};
	